#ifndef __PROCESS_POOL_HPP__
#define __PROCESS_POOL_HPP__

#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
#include <vector>
#include <functional>
#include <ctime>
#include "Task.hpp"

int gprocess_default_num = 5;
using callback_t = std::function<void(int fd)>;

class Channel
{
public:
    Channel()
    {
    }
    Channel(int fd, std::string &name, pid_t id)
        : _wfd(fd), _name(name), _sub_target(id)
    {
    }
    void Debug()
    {
        printf("channel name :%s , wfd: %d , target+pid : %d \n", _name.c_str(), _wfd, _sub_target);
    }
    ~Channel()
    {
    }
    int Fd() { return _wfd; }
    std::string Name() { return _name; }
    pid_t Target() { return _sub_target; }
    void Close() { close(_wfd); }
    void Wait() { waitpid(_sub_target, nullptr, 0); }

private:
    int _wfd;          // 写端文件描述符
    std::string _name; // 进程的名称
    pid_t _sub_target; // 子进程的pid
};

class ProcessPool
{
private:
    void CtrlSubProcessHelper(int &index)
    {
        int who = index;
        index++;
        index %= _channels.size();

        // 父进程向管道中写入任务码，随机写
        int x = rand() % tasks.size();

        std::cout << "选择信道：" << _channels[who].Fd() << " , " << _channels[who].Name() << ", target: " << _channels[who].Target() << std::endl;
        write(_channels[who].Fd(), &x, sizeof(x));
    }
public:
    ProcessPool(int num = gprocess_default_num) : _processnum(num)
    {
        // 设置随机种子
        srand(time(NULL) ^ getpid() ^ 0x77);
    }
    ~ProcessPool()
    {
    }
    // 初始化channels
    bool InitProcessPool(callback_t cb)
    {
        for (int i = 0; i < _processnum; i++)
        {
            // 创建管道
            int pipefd[2] = {0};
            int n = pipe(pipefd);
            if (n < 0)
                return false;
            // {
            //     perror("pipe");
            //     exit(1);
            // }

            // 创建子进程
            pid_t id = fork();
            if (id < 0)
                return false;
            // {
            //     perror("fork");
            //     exit(1);
            // }
            else if (id == 0)
            {
                // 子进程
                // 关闭自己不用的文件描述符，子进程此处是读端
                close(pipefd[1]); // 关闭写端
                //关闭从父进程哪里继承下来的写端
                for(auto e: _channels)
                {
                    e.Close();
                }

                // 自己成做自己的任务
                //  sleep(10);
                cb(pipefd[0]);

                // 子进程退出
                exit(0);
            }

            // 父进程
            // 父进程关闭自己不用的文件描述符，此处父进程为写端，关闭父进程的读端
            close(pipefd[0]);
            // 父进程将所有创建的子进程进行组织
            std::string name = "Channel - " + std::to_string(i);
            // Channel channel(pipefd[1] , name , id);
            // channels.push_back(channel);
            // 可以直接使用emplace_back
            _channels.emplace_back(pipefd[1], name, id);
            sleep(1);
        }
        return true;
    }

    // InitProcessPool(channels , [](int fd){
    //     while(true)
    //     {
    //         int code = 0;
    //         std::cout << "子进程阻塞: " <<  getpid() << std::endl;
    //         ssize_t n = read(fd , &code , sizeof(code));
    //         if(n > 0)
    //         {
    //             std::cout << "子进程被唤醒: " << getpid() << std::endl;
    //         }
    //     }
    // });//初始化

    // 父进程
    // 有了channels , 那么父进程对于子进程的控制就可以转换为对于channels 的控制
    //  sleep(20);
    // debug
    // for (auto e : channels)
    // {
    //     e.Debug();
    // }

    // sleep(3);
    // std::cout << "父进程开始控制" << std::endl;
    // 父进程控制子进程退出的方式有多种,函数重载
    // void PollingCtrlSubProcess(int count)
    // {
    //     if (count < 0)
    //         return;
    //     int index = 0;
    //     while (count)
    //     {
    //         int who = index;
    //         index++;
    //         index %= _channels.size();
    //         int x = 1;
    //         std::cout << "选择信道：" << _channels[who].Fd() << " , " << _channels[who].Name() << ", target: " << _channels[who].Target() << std::endl;
    //         write(_channels[who].Fd(), &x, sizeof(x));
    //         // sleep(1);
    //         count--;
    //     }
    // }

    void PollingCtrlSubProcess(int count)
    {
        if (count < 0)
            return;
        int index = 0;
        while (count)
        {
            CtrlSubProcessHelper(index);
            count--;
        }
    }

    void PollingCtrlSubProcess()
    {
        // 父进程通过对指定文件描述符做写入来唤醒子进程，为了保证负载均衡，此处选择使用轮询的方式
        int index = 0;
        while (true)
        {
            CtrlSubProcessHelper(index);
            // sleep(1);
        }
    }

    void WaitCtrlSubProcess()
    {
        // 遍历cnannels 中父进程的写端，关闭其文件描述符，然后再等待子进程
        //这样释放、等待是有问题的，除非在创建子进程的时候把不想关的文件文件描述符均关掉
        for (auto e : _channels)
        {
            e.Close();
            e.Wait();
        }
        //可以倒着回收
        // for(int i = _channels.size()-1;i>=0;i--)
        // {
        //     _channels[i].Close();
        //     _channels[i].Wait();
        // }
        // for (auto e : _channels)
        // {
        //     e.Wait();
        //     // debug
        //     std::cout << "回收子进程: " << getpid() << std::endl;
        // }
    }

private:
    // 管理子进程
    std::vector<Channel> _channels;
    int _processnum; // 子进程个数
};

#endif